'''
Created on Sep 18, 2011

@author: Almada, Correa, Ventura
'''

import ply.lex as lex
import ply.yacc as yacc
import AST_t1i as ast
from string import replace

reserved = {
            'int' : 'INT',
            'bool' : 'BOOL',
            'num' : 'NUM',
            'str' : 'STR', 
            'string' : 'STRING',
            'while' : 'WHILE',
            'repeat' : 'REPEAT',
            'if' : 'IF',
            'then' : 'THEN',
            'else' : 'ELSE',
            'true' : 'TRUE',
            'false' : 'FALSE', 
            'write' : 'WRITE',
            'read' : 'READ',
            'length' : 'LENGTH'
            }

tokens = (
          'BOOL', 'INT', 'NUM', 'STR', 'STRING',
          'TRUE', 'FALSE',
          'IGUAL', 'MENOR', 'MAYOR', 'MENORIGUAL', 'MAYORIGUAL', 'DIFERENTE', 
          'NOT', 'AND', 'OR', 
          'SUMA', 'RESTA', 'DIVISION', 'MULTIPLICACION', 'MOD',
          'WHILE', 'REPEAT',
          'IF', 'THEN', 'ELSE',
          'ABREPAREN', 'CIERRAPAREN', 'ABRELLAVE', 'CIERRALLAVE',
          'PYC', 'DOSPUNTOSIGUAL', 'VARIABLE',
          'VALOR', 'VALORSTRING',
          'READ', 'WRITE', 'LENGTH'
          )

# Tokens
t_INT = r'int'
t_BOOL = r'bool'
t_NUM = r'num'
t_STR = r'str'
t_STRING = r'string'
t_WHILE = r'while'
t_REPEAT = r'repeat'
t_IF = r'if'
t_THEN = r'then'
t_ELSE = r'else'
t_TRUE = r'true'
t_FALSE = r'false'
t_WRITE = r'write'
t_READ = r'read'
t_LENGTH = r'length'
t_DOSPUNTOSIGUAL = r'\:='
t_DIFERENTE = r'\<\>'
t_MENORIGUAL = r'\<\='
t_MAYORIGUAL = r'\>\='
t_IGUAL = r'\='
t_MENOR = r'\<'
t_MAYOR = r'\>'
t_NOT = r'!'
t_AND = r'\&'
t_OR = r'\|'
t_SUMA = r'\+'
t_RESTA = r'\-'
t_DIVISION = r'\/'
t_MOD = r'\%'
t_MULTIPLICACION = r'\*' 
t_ABREPAREN = r'\('
t_CIERRAPAREN = r'\)'
t_ABRELLAVE = r'\{'
t_CIERRALLAVE = r'\}'
t_PYC = r';'

def t_VARIABLE(t):
    r'[a-zA-Z]+[a-zA-Z_0-9]*'
    t.type = reserved.get(t.value,'VARIABLE')    # Check for reserved words
    return t

def t_VALOR(t):
    r'[0-9]+(\.[0-9]+){0,1}'
    return t
    
def t_VALORSTRING(t):
    #r"'(^'|^\n)*'"
    r"\'([^\\\n]|(\\.))*?\'"
    t.value = t.value.replace("'",'')
    return t

# Comentarios despues de doble barra
def t_COMMENT(t):
    r'(/\*(.|\n)*\*/)|(//.*)'
    pass

# Espacios en blanco
#def t_WS(t):
#    r'[ ]+'
#    if t.lexer.at_line_start and t.lexer.paren_count == 0:
#        return t

# Si hay parentesis no importa que hayan ENTERS
def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)
    t.type = "NEWLINE"
    if t.lexer.lineno == 0:
        return t

# Caracteres ignorados
t_ignore = ' \t'

errorLex = 0
errorSin = 0

def t_error(t):
    print("Illegal character '%s'" % t.value[0])
    global errorLex
    errorLex = 1
    t.lexer.skip(1)
    
# Armar el lexer
lex.lex()

#literals = ['+','-','*','/','%',';']

precedence = (
    ('left', 'PYC'),
    ('right', 'DOSPUNTOSIGUAL'), 
    ('right', 'WRITE'),
    ('right', 'LENGTH'),
    ('left', 'READ'),
    ('left', 'VARIABLE', 'VALORSTRING', 'VALOR'), 
    ('right', 'IF'),
    ('right', 'WHILE'),
    ('left', 'REPEAT'),        
    ('right', 'BOOL', 'INT', 'NUM', 'STRING', 'STR', 'TRUE', 'FALSE'),
    ('right', 'NOT'),
    ('left', 'AND'),
    ('left', 'OR'),
    ('left', 'IGUAL', 'MAYOR', 'MAYORIGUAL', 'MENORIGUAL', 'DIFERENTE', 'MENOR'),
    ('left', 'SUMA', 'RESTA'),
    ('left', 'MULTIPLICACION', 'DIVISION', 'MOD'),
    ('right', 'ABREPAREN'),
    ('right', 'ABRELLAVE'),
    ('left', 'CIERRAPAREN'),
    ('left', 'CIERRALLAVE'),
    ('right', 'THEN'),
    ('right', 'ELSE')
)

# Reglas de parsing
def p_sentencia(p):
    'sentencia : sentencia PYC sentencia'
    p[0] = ast.Secuencia(p[1], p[3])

def p_SentenciaParen(p):
    'sentencia : ABREPAREN sentencia CIERRAPAREN'
    p[0] = ast.Sentencia(p[2], True)

def p_Bloque(p):
    'sentencia : ABRELLAVE sentencia CIERRALLAVE'
    p[0] = ast.Bloque(p[2])
 
#Asignacion de valores a variables
def p_AsignacionBool(p):
    'sentencia : BOOL VARIABLE DOSPUNTOSIGUAL sentencia'
    p[0] = ast.AsignacionBool(p[2],p[4])

def p_AsignacionInt(p):
    'sentencia : INT VARIABLE DOSPUNTOSIGUAL sentencia'
    p[0] = ast.AsignacionInt(p[2],p[4])
        
def p_AsignacionNum(p):
    'sentencia : NUM VARIABLE DOSPUNTOSIGUAL sentencia'
    p[0] = ast.AsignacionNum(p[2],p[4])
        
def p_AsignacionString(p):
    'sentencia : STRING VARIABLE DOSPUNTOSIGUAL sentencia'
    p[0] = ast.AsignacionString(p[2],p[4])

#No se realiza el chequeo de tipos. Pero asignar expresion a la variable correspondiente
#Deberiamos tener una clase que asigne sin tipo alguno
def p_Asignacion(p):
    'sentencia : VARIABLE DOSPUNTOSIGUAL sentencia'
    p[0] = ast.Asignacion(p[1],p[3])
    
#Declaracion de variables               
def p_DeclaracionBool(p):
    'sentencia : BOOL VARIABLE'
    p[0] = ast.DeclaracionBool(p[2])
     
def p_DeclaracionInt(p):
    'sentencia : INT VARIABLE'
    p[0] = ast.DeclaracionInt(p[2])
        
def p_DeclaracionNum(p):
    'sentencia : NUM VARIABLE'
    p[0] = ast.DeclaracionNum(p[2])

def p_DeclaracionString(p):
    'sentencia : STRING VARIABLE'
    p[0] = ast.DeclaracionString(p[2])
    
def p_IteracionElse(p):
    'sentencia : WHILE sentencia REPEAT sentencia ELSE sentencia'
    p[0] = ast.IteracionElse(p[2], p[4], p[6])

def p_Iteracion(p):
    'sentencia : WHILE sentencia REPEAT sentencia'
    p[0] = ast.Iteracion(p[2], p[4])
        
def p_CondicionalElse(p):
    'sentencia : IF sentencia THEN sentencia ELSE sentencia'
    p[0] = ast.CondicionalElse(p[2], p[4], p[6])
        
def p_Condicional(p):
    'sentencia : IF sentencia THEN sentencia'
    p[0] = ast.Condicional(p[2], p[4])

# OPERACIONES DE PRECEDENCIA
def p_Igual(p):
    'sentencia : sentencia IGUAL sentencia'
    p[0] = ast.Igual(p[1], p[3])

def p_Diferente(p):
    'sentencia : sentencia DIFERENTE sentencia'
    p[0] = ast.Diferente(p[1], p[3])
   
def p_Menor(p):
    'sentencia : sentencia MENOR sentencia'
    p[0] = ast.Menor(p[1], p[3])

def p_Mayor(p):
    'sentencia : sentencia MAYOR sentencia'
    p[0] = ast.Mayor(p[1], p[3])

def p_MenorIgual(p):
    'sentencia : sentencia MENORIGUAL sentencia'
    p[0] = ast.MenorIgual(p[1], p[3])

def p_MayorIgual(p):
    'sentencia : sentencia MAYORIGUAL sentencia'
    p[0] = ast.MayorIgual(p[1], p[3])

# OPERACIONES BOOLEANAS
def p_Not(p):
    'sentencia : NOT sentencia'
    p[0] = ast.Not(p[2]) 

def p_And(p):
    'sentencia : sentencia AND sentencia'
    p[0] = ast.And(p[1], p[3]) 

def p_Or(p):
    'sentencia : sentencia OR sentencia'
    p[0] = ast.Or(p[1], p[3])

# EXPRESIONES ARITMETICAS
def p_Suma(p):
    'sentencia : sentencia SUMA sentencia'
    p[0] = ast.Suma(p[1], p[3])
    
def p_Resta(p):
    'sentencia : sentencia RESTA sentencia'
    p[0] = ast.Resta(p[1], p[3])

def p_Multiplicacion(p):
    'sentencia : sentencia MULTIPLICACION sentencia'
    p[0] = ast.Multiplicacion(p[1], p[3])
    
def p_Division(p):
    'sentencia : sentencia DIVISION sentencia'
    p[0] = ast.Division(p[1], p[3])
    
def p_Modulo(p):
    'sentencia : sentencia MOD sentencia'
    p[0] = ast.Modulo(p[1], p[3])

#Las operaciones de cast no llevan parentesis
def p_CastIntVal(p):
    'sentencia : INT VALOR'
    p[2] = ast.Valor(p[2])
    p[0] = ast.CastInt(p[2])
    
def p_CastIntString(p):
    'sentencia : INT VALORSTRING'
    p[2] = ast.ValorString(p[2])
    p[0] = ast.CastInt(p[2])
    
def p_CastNumVal(p):
    'sentencia : NUM VALOR'
    p[2] = ast.Valor(p[2])
    p[0] = ast.CastNum(p[2])

def p_CastNumString(p):
    'sentencia : NUM VALORSTRING'
    p[2] = ast.ValorString(p[2])
    p[0] = ast.CastInt(p[2])

def p_CastBoolString(p):
    'sentencia : BOOL VALORSTRING' 
    p[0] = ast.CastBool(p[2])
    
def p_CastStrVal(p):
    'sentencia : STR VALOR' 
    p[2] = ast.Valor(p[2])
    p[0] = ast.CastTexto(p[2])
    
def p_CastStrBool(p):
    '''sentencia : STR VALORSTRING
                 | STR TRUE
                 | STR FALSE''' 
    p[2] = ast.ValorString(p[2])
    p[0] = ast.CastTexto(p[2])
    
def p_Write(p):
    'sentencia : WRITE sentencia'
    p[0] = ast.Write(p[2])
    
def p_Read(p):
    'sentencia : READ'
    p[0] = ast.Read()

def p_Length(p):
    'sentencia : LENGTH sentencia'
    p[0] = ast.Length(p[2])

def p_CondicionBool(p):
    '''sentencia : TRUE
                 | FALSE'''
    p[0] = ast.ValorString(p[1])

def p_SentenciaValor(p):
    'sentencia : VALOR'
    p[0] = ast.Valor(p[1])
    
def p_Cadena(p):
    'sentencia : VALORSTRING'
    p[0] = ast.ValorString(p[1])
    
def p_Variable(p):
    'sentencia : VARIABLE'
    p[0] = ast.Variable(p[1])

def p_error(t):
    if hasattr(t, 'value'):
        print("Error de sintaxis en '%s'" % t.value)
    else:
        print ("Error de sintaxis no identificado")
    global errorSin
    errorSin = 1
    return "Error de sintexis"

def t1i_getAST(s):
    return yacc.yacc().parse(s)

#TODO: Implementacion del parser. En caso de exitir un error, la variable res llevara cargado el error producido (Excepciones).    
def ejecutar(code):
    try:
        arbol = t1i_getAST(code)
        if(arbol!=None):
            try:
                operacion = arbol.operar()
                global errorLex
                global errorSin
                if (errorLex == 0):
                    if (errorSin == 0):
                        msgResult = "OK"
                        res = str(operacion)
                    else:
                        msgResult = "ER"
                        res = "Error sintactico"
                        errorSin = 0
                else:
                    msgResult = "ER"
                    res = "Error lexico"
                    errorLex = 0
                    errorSin = 0
            except:
                msgResult="ER"
                res = "Error al operar"
        else:
            msgResult="ER"
            res = "Arbol vacio"
    except:
        msgResult="ER"
        res = "Error al parsear"
    result = msgResult + "#" + res
    return result  

def ejecutar_prueba(code):
    try:
        arbol = t1i_getAST(code)
        if(arbol!=None):
            try:
                print arbol.mostrar()
                arbol.operar()
            except:
                msgResult="ER"
                res = "Error al operar"
                print msgResult + "#" + res
        else:
            msgResult="ER"
            res = "Arbol vacio"
            print msgResult + "#" + res
    except:
        msgResult="ER"
        res = "Error al parsear"
        print msgResult + "#" + res
        
def ejecutar_prueba_unitaria():
    code=raw_input("Ingrese nueva linea: ")
    ejecutar_prueba(code)
    
#fromStdln()
if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testStdIn']
    ejecutar_prueba_unitaria() 
